/*
 * @Author: Jiraiya
 * @Date: 2020-05-22 14:37:33
 * @LastEditors: Jiraiya
 * @LastEditTime: 2020-05-22 14:46:25
 * @Description: 
 */ 

 const {escapeUserProvidedKey} = require('./utils');
 const {getPooledTraverseContext} = require('./pool');
/**
 * @description: 
 * @param {Array | Object} children 子组件
 * @param {Function} func 回调函数
 * @param {Object} context 上下文 (回调中的this)
 * @return: 
 */
function mapChildren(children, func, context){
    if(children == null) return children;
    const result = [];
    mapIntoWithKeyPrefixInternal(children, result, null, func, context);
    return result;
}


/**
 * @description: 
 * @param {type} 
 * @return: 
 */
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context){
    let escapedPrefix = '';
    if(prefix !== null){
        // 校验key合法性
        escapedPrefix = escapeUserProvidedKey(prefix) + '/';
    }

    // 从变量池获取一个变量
    const traverseContext = getPooledTraverseContext(
        array,
        escapedPrefix,
        func,
        context,
    )
    //处理所有子节点
    traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
}

/**
 * @description: 处理所有子节点
 * @param {type} 
 * @return: 
 */
function traverseAllChildren(children, callback, traverseContext) {
    if (children == null) {
        return 0
    }
    return traverseAllChildrenImpl(children, '', callback, traverseContext)
}

function traverseAllChildrenImpl(
    children,
    nameSoFar,
    callback,
    traverseContext,
  ) {
    const type = typeof children;
  
    if (type === 'undefined' || type === 'boolean') {
      children = null;
    }
  
    let invokeCallback = false;
  
    if (children === null) {
      invokeCallback = true;
    } else {
      switch (type) {
        case 'string':
        case 'number':
          invokeCallback = true;
          break;
        case 'object':
          switch (children.$$typeof) {
            case REACT_ELEMENT_TYPE:
            case REACT_PORTAL_TYPE:
              invokeCallback = true;
          }
      }
    }
  
    if (invokeCallback) {
      callback(
        traverseContext,
        children,
        nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
      );
      return 1;
    }
  
    let child;
    let nextName;
    let subtreeCount = 0; // Count of children found in the current subtree.
    const nextNamePrefix =
      nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
  
    if (Array.isArray(children)) {
      for (let i = 0; i < children.length; i++) {
        child = children[i];
        nextName = nextNamePrefix + getComponentKey(child, i);
        subtreeCount += traverseAllChildrenImpl(
          child,
          nextName,
          callback,
          traverseContext,
        );
      }
    } else {
    //   const iteratorFn = getIteratorFn(children);
    //   if (typeof iteratorFn === 'function') {  
    //     const iterator = iteratorFn.call(children);
    //     let step;
    //     let ii = 0;
    //     while (!(step = iterator.next()).done) {
    //       child = step.value;
    //       nextName = nextNamePrefix + getComponentKey(child, ii++);
    //       subtreeCount += traverseAllChildrenImpl(
    //         child,
    //         nextName,
    //         callback,
    //         traverseContext,
    //       );
    //     }
    //   } else if (type === 'object') {
    //     let addendum = '';
    //     const childrenString = '' + children;
    //   }
    }
  
    return subtreeCount;
}



/**
 * @description: 单个节点的处理
 * @param {type} 
 * @return: 
 */
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
    const {
      result,
      keyPrefix,
      func,
      context
    } = bookKeeping;
  
    let mappedChild = func.call(context, child, bookKeeping.count++);
    if (Array.isArray(mappedChild)) {
      mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
    } else if (mappedChild != null) {
      if (isValidElement(mappedChild)) {
        mappedChild = cloneAndReplaceKey(
          mappedChild,
          // Keep both the (mapped) and old keys if they differ, just as
          // traverseAllChildren used to do for objects as children
          keyPrefix +
          (mappedChild.key && (!child || child.key !== mappedChild.key) ?
            escapeUserProvidedKey(mappedChild.key) + '/' :
            '') +
          childKey,
        );
      }
      result.push(mappedChild);
    }
}

